﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Collections;
using ORM.Annotations;
using Domain.Commons;
using System.Configuration;


namespace DBUtils
{
    public interface IDataRowMapper<T> where T : class
    {
        IList<T> MapRow(IDataReader reader);
    }

    public abstract class DBWrapper : IDBWrapper
    {
        public const string ConfigKey_DataBase = "DBConnectionString";
        public static readonly String DBConnectionString = ConfigurationManager.ConnectionStrings[ConfigKey_DataBase].ConnectionString;

        public const string ConfigKey_DBType = "DBType";
        public static readonly String DBDialect = ConfigurationManager.ConnectionStrings[ConfigKey_DBType].ConnectionString;

        protected IDbConnection connection;
        protected IDbCommand dbcommand;

        public static IDBWrapper OpenWrapper()
        {
            if (!String.IsNullOrEmpty(DBDialect))
            {
                if (String.Equals("MSSQL", DBDialect, StringComparison.OrdinalIgnoreCase) || String.Equals("SQLServer", DBDialect, StringComparison.OrdinalIgnoreCase))
                {
                    return new SqlDBWrapper();
                }
                else if (String.Equals("MySQL", DBDialect, StringComparison.OrdinalIgnoreCase))
                {
                    return new MySqlDBWrapper();
                }
            }
            throw new EntryPointNotFoundException("未找到所支持的数据类型配置，现支持类型：MSSQL和MySQL.");
        }
        

        public abstract IDbConnection CreateConnection();

        /// <summary>
        /// Create a current database DBWrapper instance.
        /// </summary>
        /// <returns></returns>
        public static DBWrapper CreateCurrentDBWrapper()
        {
            return new SqlDBWrapper();
        }

        #region Db Transaction
        private IDbTransaction dbTransaction;

        protected IDbTransaction DbTransaction
        {
            get { return dbTransaction; }
        }

        public void BeginTransaction()
        {
            this.dbTransaction = GetConnection().BeginTransaction();
            this.dbcommand = GetConnection().CreateCommand();
            if (null == this.dbcommand.Transaction)
            {
                this.dbcommand.Transaction = this.dbTransaction;
            }
        }

        public void Commit()
        {
            if (null != this.dbTransaction)
            {
                this.dbTransaction.Commit();
            }
        }

        public void RollBack()
        {
            if (null != this.dbTransaction)
            {
                this.dbTransaction.Rollback();
            }
        }

        #endregion

        public IDbCommand GetDbCommand()
        {
            if (null == this.dbcommand)
            {
                this.dbcommand = this.GetConnection().CreateCommand();
            }
            this.dbcommand.CommandText = String.Empty;
            this.dbcommand.Parameters.Clear();
            return this.dbcommand;
        }

        public IDbConnection GetConnection()
        {
            if (null == this.connection)
            {
                this.connection = CreateConnection();
            }
            if (null == this.connection)
            {
                throw new NotImplementedException("The method of abstract create connection has not implemented in sub class,implement it please.");
            }
            if (this.connection.State != ConnectionState.Open)
            {
                this.connection.Open();
            }
            return this.connection;
        }

        public int ExecuteNonQuery(String sqlString)
        {
            return ExecuteNonQuery(sqlString, null);
        }

        public int ExecuteNonQuery(string sqlString, params IDataParameter[] parameters)
        {
            if (String.IsNullOrEmpty(sqlString))
            {
                return 0;
            }
            Console.WriteLine("SQL : " + sqlString);
            IDbCommand command = GetDbCommand();
            command.CommandText = sqlString;
            if (null != parameters)
            {
                foreach (IDataParameter entry in parameters)
                {
                    if (null != entry)
                    {
                        command.Parameters.Add(entry);
                    }
                }
            }
            return command.ExecuteNonQuery();
        }

        public T QueryScalar<T>(string sqlString) where T : class
        {
            return QueryScalar<T>(sqlString, (IDataParameter[])null);
        }

        public T QueryScalar<T>(string sqlString, params IDataParameter[] parameters) where T : class
        {
            if (String.IsNullOrEmpty(sqlString))
            {
                return default(T);
            }
            IDbCommand command = GetDbCommand();
            command.CommandText = sqlString;
            if (null != parameters)
            {
                foreach (IDataParameter entry in parameters)
                {
                    if (null != entry)
                    {
                        command.Parameters.Add(entry);
                    }
                }
            }
            object result = command.ExecuteScalar();
            if (result is T)
            {
                return result as T;
            }
            return default(T);
        }

        public IDataReader Query(String sqlString, params IDataParameter[] parameters)
        {
            if (String.IsNullOrEmpty(sqlString))
            {
                return null;
            }
            Console.WriteLine("SQL : " + sqlString);
            IDbCommand command = GetDbCommand();
            command.CommandText = sqlString;
            if (null != parameters && parameters.Length > 0)
            {
                foreach (IDataParameter entry in parameters)
                {
                    if (null != entry)
                    {
                        command.Parameters.Add(entry);
                    }
                }
            }
            return command.ExecuteReader();
        }

        public int QueryForInt(string sqlString, params IDataParameter[] parameters)
        {
            object result = QueryForObject(sqlString, parameters);
            return null == result ? 0 : Convert.ToInt32(result);
        }

        public object QueryForObject(string sqlString, params IDataParameter[] parameters)
        {
            IDataReader reader = Query(sqlString, parameters);
            if (reader.Read())
            {
                return reader[0];
            }
            return null;
        }

        public IList<T> Query<T>(String sqlString, IDataRowMapper<T> rowMapper, params IDataParameter[] parameters) where T : class
        {
            if (String.IsNullOrEmpty(sqlString))
            {
                return null;
            }
            if (null == rowMapper)
            {
                throw new NullReferenceException("Parameter of the IDataRowMapper interface has must be not null.");
            }
            IDbCommand command = GetDbCommand();
            command.CommandText = sqlString;
            if (null != parameters)
            {
                foreach (IDataParameter entry in parameters)
                {
                    if (null != entry)
                    {
                        command.Parameters.Add(entry);
                    }
                }
            }
            using (IDataReader reader = command.ExecuteReader())
            {
                return rowMapper.MapRow(reader);
            }
        }

        public void Dispose()
        {
            if (null != this.dbTransaction)
            {
                if (null != this.dbcommand)
                {
                    this.dbcommand.Transaction = null;
                }
                this.dbTransaction.Dispose();
            }
            if (null != this.dbcommand)
            {
                this.dbcommand.Dispose();
            }
            if (null != connection)
            {
                this.connection.Dispose();
            }
        }

        protected StringBuilder GetSelectBaseSql(IORMAnalyzer ormanalyzer)
        {
            StringBuilder sqlBuilder = new StringBuilder();
            if (null != ormanalyzer)
            {
                sqlBuilder.Append("Select ");
                if (null == ormanalyzer.FeildInfoes || ormanalyzer.FeildInfoes.Count == 0)
                {
                    sqlBuilder.Append("*");
                }
                else {
                    sqlBuilder.Append(ormanalyzer.SqlFeildsString);
                }
                sqlBuilder.Append(" From ").Append(ormanalyzer.TableInfo.Name);
            }
            return sqlBuilder;
        }

        protected T BuildInstanceByORM<T>(IDataReader reader, IORMAnalyzer analyzer) where T : class,new()
        {
            if (null != analyzer && null != reader)
            {
                if (reader.Read())
                {
                    T instance = new T();
                    foreach (EntityFeild entry in analyzer.FeildInfoes)
                    {
                        object value = reader[entry.Name];

                        if (!DBNull.Value.Equals(value))
                        {
                            entry.FeildRefInfo.SetValue(instance, value, null);
                        }
                    }
                    return instance;
                }
            }
            return default(T);
        }

        protected IList<T> BuildInstancesByORM<T>(IDataReader reader, IORMAnalyzer analyzer) where T : class,new()
        {
            if (null != reader && null != analyzer)
            {
                IList<T> results = new List<T>();
                while (true)
                {
                    T instance = BuildInstanceByORM<T>(reader, analyzer);
                    if (default(T) == instance)
                    {
                        break;
                    }
                    else
                    {
                        results.Add(instance);
                    }
                }
                return results;
            }
            return null;
        }

        public IList<T> List<T>() where T : class,new()
        {
            return List<T>(null, null);
        }

        public IList<T> List<T>(string condition,params IDataParameter[] parameters) where T : class,new()
        {
            IORMAnalyzer analyzer = new ORMAnalyzer(typeof(T));
            StringBuilder sqlBuilder = GetSelectBaseSql(analyzer);
            if (!String.IsNullOrEmpty(condition))
            {
                sqlBuilder.Append(" ").Append(condition);
            }

            IList<T> result = null;
            using (IDataReader reader = this.Query(sqlBuilder.ToString(), parameters))
            {
                if (null != reader)
                {
                    result = BuildInstancesByORM<T>(reader, analyzer);
                }
            }
            return result;
        }

        public abstract T Get<T>(object id) where T : class,new();

        public abstract int Insert(object entity);

        public abstract int Update(object entity);

        public abstract int Delete(object entity);

        public abstract int Delete<T>(object id) where T : class;

        public int Save(object entity)
        {
            if (entity is Identifer)
            {
                Identifer entry = entity as Identifer;
                if (String.IsNullOrEmpty(entry.Id))
                {
                    entry.Id = Guid.NewGuid().ToString();
                    return Insert(entry);
                }
            }

            int num = Update(entity);
            if (0 == num)
            {
                num = Insert(entity);
            }
            return num;
        }
    }
}
